﻿FolderMP3 1.2 - ADS Edition
Technische Begleitdokumentation
================================

Plattform:
    Windows 10/11 x64

Assembler:
    Flat Assembler (FASM 1)

Architektur:
    Native PE64-GUI
    nocash MP3 decoder
    Windows waveOut
    Win32/GDI
    GDI+ für Albumcover

Diese Datei ist bewusst KEIN Entwicklungs-Log.
Sie beschreibt den Aufbau von FolderMP3 1.2 und interessante technische
Entscheidungen für jemanden, der den FASM-Source verstehen oder später
weiterentwickeln möchte.


======================================================================
1. Grundidee
======================================================================

FolderMP3 ist bewusst kein Musikverwaltungsprogramm.

Das ursprüngliche Prinzip lautet:

    Der aktuelle Ordner ist die "CD".

Der Benutzer arbeitet direkt mit Ordnern und MP3-Dateien. Es gibt keine
Medienbibliothek, keine Datenbank und keine "Add Files"-Funktion.

Version 1.2 erweitert dieses Prinzip:

    Optional kann die eigene EXE beim Start selbst die erste "CD" bilden,
    wenn an ihr MP3-Dateien als NTFS Alternate Data Streams vorhanden sind.

Der normale Ordnerbetrieb bleibt vollständig erhalten.


======================================================================
2. Audioformat
======================================================================

FolderMP3 spielt bewusst ausschließlich MP3.

Der Player verwendet nicht MCI.

Die MP3-Daten werden mit dem integrierten nocash-Decoder dekodiert und als
PCM über Windows waveOut ausgegeben.

Zum getesteten Funktionsumfang gehören unter anderem:

    - CBR-MP3
    - LAME/VBR-MP3
    - Xing/LAME-Seek
    - Pause/Resume
    - Stop
    - Lautstärke
    - Auto-Next
    - Unicode-Dateinamen


======================================================================
3. Herkunft des MP3-Decoders
======================================================================

Der ursprüngliche MP3-Decoder stammt von Martin Korth (nocash).

Die FASM-Rekonstruktion und Windows-Anpassung stammt von Erdogan Tan und
basiert auf nocash MP3PLAY.EXE v1.4 / TASM source.

Für FolderMP3 wurde dieser Decoder als Teil einer nativen Win64-Anwendung
integriert und unter anderem an 64-Bit-Threads, File Mapping, waveOut,
Seek und die GUI angebunden.


======================================================================
4. Credits
======================================================================

FolderMP3 1.2

Developed by
NotCompatible (Basti) + GPT-5.6 Sol

MP3 Decoder
Original MP3 decoder/player by Martin Korth (nocash)

FASM reconstruction and Windows adaptation by Erdogan Tan
based on nocash MP3PLAY.EXE v1.4 / TASM source

Win64 port and FolderMP3 integration
NotCompatible (Basti) + GPT-5.6 Sol

Built with FASM


======================================================================
5. Native Win64-GUI
======================================================================

FolderMP3 ist ein echtes PE64-GUI-Programm.

Es gibt keine .NET-Laufzeit, kein Qt, kein SDL und kein zusätzliches
GUI-Framework.

Das Hauptfenster wird direkt mit klassischen Win32-APIs registriert und
erzeugt.

Die zentrale Fensterprozedur ist:

    WndProc

Sie verarbeitet unter anderem:

    WM_CREATE
    WM_SYSCOMMAND
    WM_COMMAND
    WM_DRAWITEM
    WM_HSCROLL
    WM_TIMER
    WM_DECODER_FINISHED
    WM_DECODER_ERROR
    WM_DESTROY

Alle normalen nicht selbst behandelten Nachrichten gehen an DefWindowProcW.


======================================================================
6. WndProc ist ein roher Win64-Callback
======================================================================

WndProc wird bewusst nah an der Microsoft-x64-ABI behandelt.

Beim Eintritt gelten die normalen Callback-Register:

    RCX = HWND
    RDX = Message
    R8  = WPARAM
    R9  = LPARAM

Die Funktion verteilt die Nachricht direkt auf eigene Zweige.

Das ist für einen Assembler-Source besonders interessant, weil die echte
Windows-x64-Aufrufkonvention sichtbar bleibt.


======================================================================
7. WM_CREATE und hMain
======================================================================

WM_CREATE trifft bereits während CreateWindowExW ein.

Das bedeutet: CreateWindowExW hat beim aufrufenden start-Code sein HWND noch
nicht zurückgegeben, obwohl WndProc bereits ausgeführt wird.

Deshalb setzt FolderMP3 bei WM_CREATE sofort:

    hMain = RCX

Erst danach werden die Kindfenster erzeugt.

Diese kleine Reihenfolge verhindert, dass OnCreate mit einem noch nicht
gesetzten Hauptfenster-Handle arbeitet.


======================================================================
8. Keine klassische Dialogressource
======================================================================

Die komplette Oberfläche wird zur Laufzeit mit CreateWindowExW aufgebaut.

Verwendete Standardklassen sind unter anderem:

    EDIT
    STATIC
    BUTTON
    LISTBOX
    COMBOBOX
    SCROLLBAR

Die Oberfläche hängt nicht von einer .RC-Datei ab.

Auch die Schrift wird nicht eingebettet. FolderMP3 erzeugt Segoe UI über
CreateFontW.


======================================================================
9. Titelleiste und Credits sind getrennt
======================================================================

Die sichtbare Titelleiste ist bewusst kurz:

    FolderMP3 1.2 - ADS Edition

Technische Credits stehen nicht mehr in der Caption.

Sie befinden sich stattdessen im About-Dialog.

Dadurch bleibt die normale Windows-Titelleiste übersichtlich.


======================================================================
10. Der About-Dialog ist kein eigenes Fenster
======================================================================

Der About-Button erzeugt keinen registrierten Dialog und besitzt keine
eigene About-WndProc.

Der komplette Dialog ist:

    MessageBoxW(
        hMain,
        aboutText,
        aboutTitle,
        MB_OK | MB_ICONINFORMATION
    )

Windows erzeugt und verwaltet dieses Fenster selbst.

Das spart Code und Ressourcen und passt zum minimalistischen Aufbau.


======================================================================
11. Kompaktes festes Layout
======================================================================

Die Oberfläche ist bewusst kompakt und nicht als frei skalierbarer
Medienplayer gedacht.

Wichtige Bereiche sind:

    Titelfeld links oben
    kleines Albumcover rechts oben
    Zeit und Positionsregler
    fünf Transportbuttons
    Lautstärkeregler
    große Playlist
    einklappbarer Ordnerbrowser

Der Browser verändert die Höhe des Hauptfensters, statt ein zweites
Hauptfenster zu öffnen.


======================================================================
12. Owner-Draw-Transportbuttons
======================================================================

Die Transportbuttons sind normale Windows-BUTTON-Controls, werden aber mit:

    BS_OWNERDRAW

erzeugt.

Windows sendet deshalb WM_DRAWITEM an WndProc.

WndProc ruft für die Transportknöpfe:

    DrawTransportButton

auf.


======================================================================
13. Wie die Transportbuttons gezeichnet werden
======================================================================

DrawTransportButton verwendet nur klassische GDI-Funktionen.

Hintergrund:

    GetSysColorBrush(COLOR_BTNFACE)
    FillRect

Rahmen:

    GetSysColorBrush(COLOR_3DSHADOW)
    FrameRect

Die Symbole bestehen aus echter Geometrie.

Play:
    Polygon-Dreieck

Pause:
    zwei FillRect-Rechtecke

Stop:
    ein FillRect-Quadrat

Zurück / Weiter:
    Dreieck plus schmaler Balken

Danach wird die Beschriftung mit DrawTextW rechts vom Symbol gezeichnet.

Es gibt dafür keine Button-Bitmaps und keine Icon-Dateien.


======================================================================
14. Playlist als normale LISTBOX
======================================================================

Die Playlist ist eine Standard-LISTBOX.

Sie wird sowohl für normale Ordner-MP3s als auch für EXE-ADS-MP3s benutzt.

Dadurch mussten Next, Previous und Auto-Next für Version 1.2 nicht als
zweites System neu geschrieben werden.

Die Quelle ändert sich, die sichtbare Playlist bleibt dieselbe.


======================================================================
15. Unicode im ganzen Dateipfad
======================================================================

FolderMP3 benutzt die Wide-/Unicode-Varianten der Windows-APIs.

Beispiele:

    CreateWindowExW
    GetModuleFileNameW
    FindFirstFileW
    FindNextFileW
    FindFirstStreamW
    FindNextStreamW
    CreateFileW
    SetWindowTextW
    MessageBoxW

Pfadpuffer sind UTF-16-Puffer.

Damit funktionieren auch Dateinamen außerhalb alter ANSI-Codepages.


======================================================================
16. Normale Ordner-Playlist
======================================================================

LoadPlaylist durchsucht:

    currentFolder\*.mp3

mit:

    FindFirstFileW
    FindNextFileW

Verzeichniseinträge werden übersprungen.

Gefundene MP3-Dateinamen werden in die bestehende Playlist-LISTBOX
eingetragen.


======================================================================
17. Der EXE-Ordner und der vollständige EXE-Pfad
======================================================================

Für den ursprünglichen Ordnerbetrieb genügte der Ordner der EXE.

Die ADS-Funktion benötigt zusätzlich den vollständigen Pfad zur
ausführbaren Datei.

Deshalb existieren getrennt:

    exePath
        vollständiger Pfad zur EXE

    exeDir
        Verzeichnis der EXE

    currentFolder
        aktive normale Ordner-"CD"

    browserFolder
        momentan im Browser betrachteter Ordner

Diese Trennung ist wichtig, weil alle vier Werte unterschiedliche Aufgaben
haben.


======================================================================
18. Neuer Playlist-Modus in Version 1.2
======================================================================

Version 1.2 kennt zwei interne Quellen:

    PLAYLIST_FOLDER  = 0
    PLAYLIST_EXE_ADS = 1

Der aktuelle Zustand steht in:

    playlistMode

Zusätzlich zählt:

    embeddedTrackCount

wie viele MP3-Streams an der eigenen EXE gefunden wurden.


======================================================================
19. Was ein NTFS Alternate Data Stream hier bedeutet
======================================================================

NTFS kann neben dem normalen Inhalt einer Datei benannte Datenströme
speichern.

Für FolderMP3 kann deshalb logisch ein Pfad existieren wie:

    C:\Player\FolderMP3.exe:track.mp3

Die EXE bleibt die Trägerdatei; track.mp3 ist ein benannter Datenstrom
dieser Datei.

FolderMP3 1.2 verwendet diese Streams direkt als optionale Musikquelle.


======================================================================
20. Stream-Enumeration
======================================================================

LoadEmbeddedPlaylist enumeriert die Streams der eigenen EXE mit:

    FindFirstStreamW
    FindNextStreamW
    FindClose

Dafür wird der vollständige exePath verwendet.

Die Windows-API liefert kanonische Streamnamen wie:

    :intro.mp3:$DATA


======================================================================
21. Nicht jeder ADS wird als Musik akzeptiert
======================================================================

Die Routine:

    IsMp3NamedDataStream

prüft, ob ein gefundener Stream auf:

    .mp3:$DATA

endet.

Die relevante Prüfung ist case-insensitiv.

Beispiele:

    :intro.mp3:$DATA        akzeptiert
    :INTRO.MP3:$DATA        akzeptiert
    :Cover.jpg:$DATA        ignoriert
    :Zone.Identifier:$DATA  ignoriert
    :notes.txt:$DATA        ignoriert

Dadurch können andere benannte Streams an derselben EXE existieren, ohne
in der Musikplaylist aufzutauchen.


======================================================================
22. Sichtbarer ADS-Name
======================================================================

Windows liefert beispielsweise:

    :intro.mp3:$DATA

In der Playlist soll aber nur:

    intro.mp3

stehen.

Dafür existiert:

    CopyNamedStreamDisplayName

Die Routine entfernt für die Anzeige das führende ":" und das abschließende
":$DATA".


======================================================================
23. LoadEmbeddedPlaylist
======================================================================

LoadEmbeddedPlaylist arbeitet sinngemäß so:

1. Playlist leeren.
2. embeddedTrackCount auf 0 setzen.
3. exePath sicherstellen.
4. Streams der eigenen EXE enumerieren.
5. Nur MP3-Datenstreams akzeptieren.
6. Anzeigenamen erzeugen.
7. Namen in die bestehende ListBox eintragen.
8. Treffer mitzählen.
9. Stream-Suchhandle schließen.

Bei mindestens einem Treffer:

    playlistMode = PLAYLIST_EXE_ADS

Der erste Eintrag wird ausgewählt.

Es startet aber noch keine Wiedergabe.


======================================================================
24. ADS haben beim Programmstart Vorrang
======================================================================

OnCreate versucht zuerst:

    LoadEmbeddedPlaylist

Wenn mindestens ein geeigneter MP3-ADS gefunden wird, bleibt diese
Playlist aktiv.

Nur wenn kein MP3-ADS vorhanden ist, wird:

    playlistMode = PLAYLIST_FOLDER
    LoadPlaylist

verwendet.

Damit ist die ADS-Funktion vollständig optional.


======================================================================
25. Kein Autoplay
======================================================================

"ADS zuerst" bedeutet nicht "ADS automatisch abspielen".

Der erste Playlist-Eintrag wird lediglich ausgewählt.

Der Benutzer startet den Titel wie bisher mit:

    Play

oder:

    Doppelklick auf einen Playlist-Eintrag.


======================================================================
26. PlaySelected entscheidet nur über die Quelle
======================================================================

PlaySelected liest weiterhin den sichtbaren Dateinamen aus derselben
ListBox.

Danach gibt es zwei Arten der Pfadbildung.

ADS-Modus:

    fullPath = exePath
    fullPath += ":"
    fullPath += fileName

Ergebnis zum Beispiel:

    C:\Player\FolderMP3.exe:intro.mp3

Ordner-Modus:

    fullPath = currentFolder
    fullPath += "\"
    fullPath += fileName

Ab:

    .source_ready

verwenden beide Quellen wieder denselben Playerpfad.


======================================================================
27. Kein zweites Audio-Subsystem für ADS
======================================================================

Die ADS-Erweiterung ist primär eine neue Dateiquelle.

Es gibt keinen speziellen ADS-Decoder und keinen ADS-waveOut-Pfad.

Nach der Pfadbildung benutzen beide Modi dieselben vorhandenen Komponenten:

    LoadCoverForPath
    DecoderThreadProc
    File Mapping
    nocash MP3 decoder
    waveOut
    Seek
    Lautstärke
    Auto-Next

Das hält die Erweiterung klein und reduziert neue Fehlerquellen.


======================================================================
28. Direkte Wiedergabe aus owner.exe:track.mp3
======================================================================

ADS-Musik wird nicht in eine temporäre Datei exportiert.

Der Decoder erhält den echten NTFS-Stream-Pfad.

Windows-Datei-APIs können diesen Pfad direkt öffnen.

Dadurch bleibt der bestehende Datei- und Decodercode nutzbar.


======================================================================
29. Klick auf "Ordner" beendet den ADS-Modus
======================================================================

Im ADS-Startmodus besitzt der vorhandene Ordner-Button eine zusätzliche
Bedeutung.

Beim ersten Klick wird zunächst:

    SwitchToFolderPlaylist

aufgerufen.

Erst danach läuft:

    ToggleFolderBrowser

wie gewohnt weiter.


======================================================================
30. SwitchToFolderPlaylist
======================================================================

SwitchToFolderPlaylist wechselt kontrolliert von PLAYLIST_EXE_ADS zu
PLAYLIST_FOLDER.

Ein eventuell laufender ADS-Titel wird nicht einfach durch eine neue
ListBox ersetzt.

Stattdessen wird zuerst der vorhandene saubere Stop-Pfad benutzt:

    DecoderStopAndWait

Danach werden Wiedergabe-, Seek- und GUI-Zustände zurückgesetzt und die
normale Ordnerplaylist geladen.


======================================================================
31. Kein Rücksprung zu ADS innerhalb derselben Sitzung
======================================================================

Nach dem Wechsel ADS -> Ordner bleibt FolderMP3 für diese Programmsitzung
im normalen Ordner-Modus.

Es gibt keinen zusätzlichen "ADS"-Button.

Nach einem Neustart wird LoadEmbeddedPlaylist erneut ausgeführt und die
EXE-ADS werden wieder bevorzugt, falls sie vorhanden sind.


======================================================================
32. Next und Previous benötigen keine ADS-Sonderlogik
======================================================================

NextTrack und PreviousTrack arbeiten nur mit der bestehenden Playlist.

Die Pfadquelle wird erst in PlaySelected entschieden.

Deshalb funktionieren zyklisches Next/Previous sowohl mit Ordnerdateien
als auch mit ADS-Dateien.


======================================================================
33. Auto-Next funktioniert ebenfalls quellenunabhängig
======================================================================

Auch Auto-Next startet letztlich einen neuen Playlist-Eintrag über den
normalen Playerpfad.

Da playlistMode beim tatsächlichen Start den vollständigen Pfad bestimmt,
ist keine zweite ADS-Auto-Next-Implementierung nötig.


======================================================================
34. Der Ordnerbrowser ist bewusst kein echter TreeView
======================================================================

Der hierarchisch aussehende Browser ist technisch eine normale LISTBOX.

Der Baum wird nur optisch erzeugt.

BuildBrowserTreeLine verwendet Unicode-Zeichen wie:

    U+2514  └
    U+251C  ├
    U+2500  ─

sowie Einrückungen.

Damit entsteht eine TreeView-ähnliche Darstellung ohne die zusätzliche
Zustandsverwaltung eines echten TreeView-Controls.


======================================================================
35. browserFolder und currentFolder bleiben getrennt
======================================================================

browserFolder ist nur der gerade betrachtete Navigationspfad.

currentFolder ist die tatsächlich aktive normale Ordner-"CD".

Dadurch kann man durch Zwischenordner navigieren, ohne die gerade laufende
Musik zu stoppen.

Diese Trennung wurde für die ADS-Erweiterung bewusst beibehalten.


======================================================================
36. Ein Browser-Klick lädt noch keine neue CD
======================================================================

Einfaches Markieren und Pfeiltasten dienen nur der Navigation.

Erst ein Doppelklick auf einen Ordner führt zu:

    ChangeFolderFromBrowser

Ein geeigneter Ordner mit MP3-Dateien kann dadurch currentFolder werden.

Leere Zwischenordner können betrachtet werden, ohne die Musikquelle sofort
zu ersetzen.


======================================================================
37. Wechsel über den Browser setzt eindeutig Ordner-Modus
======================================================================

Wenn ChangeFolderFromBrowser eine normale Ordner-CD übernimmt, wird:

    playlistMode = PLAYLIST_FOLDER

gesetzt.

Damit gibt es nach einem echten Ordnerwechsel keinen mehrdeutigen
Quellenzustand.


======================================================================
38. Laufwerksauswahl
======================================================================

FolderMP3 baut eine Drive-Combo aus den vorhandenen Laufwerken auf.

Bei lokalen Laufwerken wird nach Möglichkeit zusätzlich der Volume-Name
angezeigt.

Netzwerk-Laufwerke werden bei bestimmten Zusatzabfragen bewusst vorsichtig
behandelt, damit eine nicht erreichbare Netzwerkressource die GUI nicht
unnötig blockiert.


======================================================================
39. Always on Top im nativen Systemmenü
======================================================================

Version 1.1.1 ergänzte:

    Always on Top

nicht als eigenen Button, sondern im normalen Windows-Systemmenü des
Hauptfensters.

OnCreate holt:

    GetSystemMenu(hMain, FALSE)

und hängt an:

    Separator
    Always on Top

Der Rest des Systemmenüs bleibt vollständig Windows-eigen.


======================================================================
40. Warum IDM_ALWAYS_ON_TOP maskiert wird
======================================================================

Der eigene Systembefehl verwendet:

    IDM_ALWAYS_ON_TOP = 01FF0h

Bei WM_SYSCOMMAND sind die unteren vier Bits von wParam für Windows
reserviert.

WndProc macht deshalb:

    and eax, 0FFF0h

bevor der Wert mit IDM_ALWAYS_ON_TOP verglichen wird.


======================================================================
41. Normale Systembefehle bleiben nativ
======================================================================

Nur der eigene Always-on-Top-Befehl wird von FolderMP3 abgefangen.

Alle anderen WM_SYSCOMMAND-Nachrichten gehen weiterhin an:

    DefWindowProcW

Damit bleiben Verschieben, Minimieren, Schließen und alle normalen
Windows-Funktionen unverändert.


======================================================================
42. Kein eigener Always-on-Top-Status
======================================================================

ToggleAlwaysOnTop führt bewusst keinen separaten Boolean.

Stattdessen liest die Routine bei jedem Klick den tatsächlichen
Extended Window Style:

    GetWindowLongPtrW(hMain, GWL_EXSTYLE)

und prüft:

    WS_EX_TOPMOST

Damit kann der interne Zustand nicht vom realen Windows-Zustand abweichen.


======================================================================
43. Topmost wird mit SetWindowPos umgeschaltet
======================================================================

Einschalten:

    HWND_TOPMOST

Ausschalten:

    HWND_NOTOPMOST

Flags:

    SWP_NOMOVE
    SWP_NOSIZE
    SWP_NOACTIVATE

Das Fenster wird dabei weder verschoben noch skaliert und erhält nicht
unnötig den Fokus.


======================================================================
44. Warum kein Pin-Button verwendet wird
======================================================================

Ein eigener Pin in der Titelleiste würde Non-Client-Zeichnung,
Hit-Testing und DWM-/Windows-10/11-Sonderfälle erfordern.

Die Systemmenü-Lösung nutzt stattdessen vollständig das bestehende native
Fensterverhalten.

Es gibt bewusst:

    kein Pin-Symbol
    kein Häkchen
    keine zusätzliche Statusanzeige
    keine eigene Caption


======================================================================
45. Wiedergabe läuft in einem Worker-Thread
======================================================================

Der MP3-Decoder läuft nicht im GUI-Thread.

PlaySelected startet:

    DecoderThreadProc

über CreateThread.

Dadurch bleiben Buttons, Browser, Scrollbars und Fensternachrichten während
des Decodierens bedienbar.


======================================================================
46. Warum der Decoder pro Track neu initialisiert wird
======================================================================

Der nocash-Decoder besitzt internen Zustand und erzeugte Tabellen.

Vor einem neuen Track werden wichtige Initialisierungswerte zurückgesetzt,
bevor mp3_init ausgeführt wird.

Dadurch kann kein alter Huffman-/Decoderzustand in den nächsten Titel
hineinwirken.


======================================================================
47. 32-Bit-Decoderpointer in einem 64-Bit-Programm
======================================================================

Der historische Decoder arbeitet an vielen Stellen mit 32-Bit-Adressen.

Statt den gesamten Decoder auf 64-Bit-Pointer umzuschreiben, wird die
MP3-Datei in einen niedrigen virtuellen Adressbereich gemappt.

Dadurch können die vorhandenen 32-Bit-Pointer weiterhin verwendet werden,
obwohl das umgebende Programm ein echtes PE64-Programm ist.

Diese Brücke ist eine der wichtigsten Eigenschaften des Win64-Ports.


======================================================================
48. Win64-ABI und Compat-Wrapper
======================================================================

Der ältere Decoderkern stammt nicht aus einer modernen x64-Codebasis.

Native Windows-APIs erwarten jedoch korrektes Stack-Alignment und die
Microsoft-x64-Aufrufkonvention.

Deshalb existieren Bridge-/Compat-Routinen für wichtige native Aufrufe.

Das betrifft besonders Dateioperationen, Threads und waveOut.


======================================================================
49. waveOut statt MCI
======================================================================

Die Audioausgabe erfolgt über WINMM/waveOut.

Wichtige Aufrufe sind unter anderem:

    waveOutOpen
    waveOutPrepareHeader
    waveOutWrite
    waveOutPause
    waveOutRestart
    waveOutReset
    waveOutUnprepareHeader
    waveOutClose
    waveOutSetVolume

Der Decoder liefert PCM und waveOut übernimmt die Ausgabe.


======================================================================
50. Legacy-WAVEHDR und nativer Win64-WAVEHDR
======================================================================

Der alte Decoder erwartet eine 32-Bit-WAVEHDR-Struktur.

Unter Win64 ist die native Struktur größer.

FolderMP3 hält deshalb eine native WAVEHDR-Seite bereit und ordnet die
Legacy-Header auf native Header ab.

Eine zentrale Routine dafür ist:

    LegacyHeaderToNative

Dadurch muss der Decoderkern nicht vollständig auf neue Strukturgrößen
umgebaut werden.


======================================================================
51. AudioCallback und Buffer-Recycling
======================================================================

waveOut meldet fertige Puffer über einen Callback.

AudioCallback verarbeitet MM_WOM_DONE und ordnet den fertigen nativen
Header wieder dem entsprechenden Decoderpuffer zu.

Danach kann der Puffer erneut mit PCM gefüllt und an waveOut übergeben
werden.

So entsteht ein kontinuierlicher Pufferkreislauf.


======================================================================
52. Stop und Threadbesitz
======================================================================

Der GUI-Thread zerlegt das waveOut-Objekt nicht gleichzeitig mit dem
Decoderthread.

DecoderStopAndWait signalisiert das Ende und wartet auf den Worker.

Der Decoderthread räumt seine Audioressourcen kontrolliert selbst auf.

Das reduziert Race-Conditions bei Stop, Trackwechsel und Seek.


======================================================================
53. Generation-ID gegen verspätete Nachrichten
======================================================================

Jeder Decoderlauf erhält eine:

    decoder_generation

FINISHED- und ERROR-Nachrichten tragen diese Generation.

WndProc kann dadurch verspätete Nachrichten eines alten Tracks ignorieren,
wenn inzwischen bereits ein neuer Track läuft.

Das ist besonders für Auto-Next und schnelle Trackwechsel wichtig.


======================================================================
54. Auto-Next-Fallback
======================================================================

Zusätzlich zur normalen Decoder-Fertigmeldung existiert ein
decoder_autonext_pending-Zustand, der vom GUI-Timer berücksichtigt wird.

Dadurch kann Auto-Next auch robust bleiben, wenn eine normale
Fertigmeldung ungünstig mit GUI-Aktivität zusammentrifft.


======================================================================
55. Positionsregler arbeitet in Promille
======================================================================

Der Positionsregler verwendet:

    0 .. 1000

statt nur 0 .. 100.

Dadurch steht beim Spulen eine feinere sichtbare Auflösung zur Verfügung.

UpdatePosition berechnet aus den tatsächlich dekodierten Frames,
Samples pro Frame und Samplerate die aktuelle Zeit.


======================================================================
56. 64-Bit-Zwischenrechnung für die Zeit
======================================================================

Die Zeitberechnung benutzt 64-Bit-Zwischenwerte.

Sinngemäß:

    Frames * Samples pro Frame * 1000 / Samplerate

Dadurch werden Überläufe vermieden, die bei längeren Titeln mit
32-Bit-Multiplikationen schneller auftreten würden.


======================================================================
57. Lange Zeitanzeige seit Version 1.1
======================================================================

WriteMMSS begrenzt die Minuten nicht mehr auf 99.

Es schreibt mindestens MM:SS, bei Bedarf aber auch mehr Minutenstellen.

Beispiele:

    04:23
    99:59
    115:37

Damit bleiben sehr lange MP3-Dateien korrekt lesbar.


======================================================================
58. Seek-Vorschau und tatsächlicher Seek
======================================================================

Während SB_THUMBTRACK wird nicht ständig der Decoder neu gestartet.

FolderMP3 aktualisiert zunächst nur die sichtbare Zielzeit.

Erst beim Loslassen bzw. beim endgültigen Scroll-Ereignis wird:

    CommitSeekPosition

ausgeführt.

Das vermeidet unnötiges permanentes Neuinitialisieren während des Ziehens.


======================================================================
59. Seek als kontrollierter Decoder-Neustart
======================================================================

Ein Seek manipuliert den laufenden Layer-III-Decoder nicht an beliebiger
Stelle.

Stattdessen:

1. Zielposition merken.
2. aktuellen Decoder sauber beenden.
3. denselben Titel neu starten.
4. im neuen Decoderlauf an die Zielregion springen.

Der bereits stabile Trackstartpfad wird also wiederverwendet.


======================================================================
60. CBR- und VBR-Seek
======================================================================

Für CBR bzw. Dateien ohne brauchbare Xing-TOC wird die Byteposition linear
aus der Audiodatenmenge geschätzt.

Für Xing/LAME-VBR-Dateien verwendet FolderMP3 die im Header enthaltene
100-Einträge-TOC.

Zwischen benachbarten Einträgen wird interpoliert.

Dadurch ist VBR-Seek wesentlich genauer als ein rein linearer Sprung nach
Dateigröße.


======================================================================
61. Warum Layer III einen Pre-Roll benötigt
======================================================================

MP3 Layer III besitzt ein Bit Reservoir und fortlaufenden Synthesezustand.

Ein Frame kann also Daten aus vorangegangenen Frames benötigen.

FolderMP3 springt deshalb nicht direkt auf das geschätzte Zielbyte.

Vor dem Ziel beginnt ein Pre-Roll.

Die vorbereitenden Frames werden vollständig dekodiert, ihr PCM wird aber
zunächst verworfen.

Dadurch können sich Bit Reservoir, IMDCT- und Synthesezustand wieder
aufbauen.


======================================================================
62. Pausezustand beim Seek
======================================================================

Wenn vor dem Seek Pause aktiv war, wird dieser Zustand gemerkt.

Nach dem Decoder-Neustart wird die Audioausgabe wieder pausiert.

Der Benutzer erlebt den Seek damit weiterhin als Bewegung innerhalb eines
pausierten Titels.


======================================================================
63. Lautstärke
======================================================================

Die Lautstärke startet bei:

    30 Prozent

Die sichtbare Scrollbar arbeitet von 0 bis 100.

Die Audioausgabe verwendet waveOutSetVolume.

Die PCM-Samples müssen dafür nicht im Decoder selbst skaliert werden.


======================================================================
64. WM_HSCROLL: Lautstärke und Seek teilen eine Nachricht
======================================================================

Sowohl Lautstärke- als auch Positionsregler sind horizontale Scrollbars.

Beide senden:

    WM_HSCROLL

OnHScroll prüft deshalb das HWND des sendenden Controls.

Nach einer Lautstärkeänderung wird der Handler ausdrücklich verlassen,
damit dieselbe Nachricht nicht versehentlich in den Seek-Pfad fällt.


======================================================================
65. Albumcover ist vom Audiodecoder getrennt
======================================================================

Die Coverfunktion verändert den nocash-Audiodecoder nicht.

Für das Bild wird die MP3-Datei separat read-only geöffnet bzw. gemappt und
nur der ID3v2-Bereich untersucht.

Die zentrale Routine ist:

    FindCoverInMappedFile


======================================================================
66. Unterstützte ID3-Cover
======================================================================

Unterstützt werden:

    ID3v2.2 PIC
    ID3v2.3 APIC
    ID3v2.4 APIC

Der Parser berücksichtigt die unterschiedlichen Framegrößenformate der
Versionen und die Beschreibung des eingebetteten Bildes.

Problematische Sondervarianten werden lieber ignoriert, statt die
Audiowiedergabe zu gefährden.


======================================================================
67. JPEG und PNG über GDI+
======================================================================

FolderMP3 enthält keinen eigenen JPEG- oder PNG-Decoder.

Die gefundenen Bildbytes werden über:

    SHCreateMemStream

als IStream bereitgestellt.

GDI+ lädt daraus mit:

    GdipLoadImageFromStream

ein GpImage.

Das spart erheblich eigenen Bilddecoder-Code.


======================================================================
68. Der IStream bleibt zusammen mit dem GpImage am Leben
======================================================================

coverGpImage und coverStream bleiben für die Lebensdauer des geladenen
Covers erhalten.

Das ist absichtlich so, weil ein GDI+-Image seinen Stream weiterhin
benötigen kann.

ClearCover gibt zuerst das Image und anschließend den Stream frei.


======================================================================
69. Kleines Cover als Owner-Draw-Control
======================================================================

Die 34x34-Coverfläche benutzt ebenfalls den Owner-Draw-Mechanismus.

WM_DRAWITEM unterscheidet anhand der Control-ID zwischen:

    Transportbutton
    Cover

DrawCoverControl zeichnet das bereits geladene GpImage mit GDI+ direkt in
den HDC des Controls.

Es gibt keinen notwendigen HBITMAP-Zwischenschritt.


======================================================================
70. DrawCoverNow
======================================================================

Zusätzlich zum normalen Repaint-Pfad existiert:

    DrawCoverNow

Die Routine holt direkt den HDC des kleinen Cover-Controls und zeichnet das
Bild synchron.

Dadurch ist die erste Anzeige nicht allein davon abhängig, wann Windows
den nächsten WM_DRAWITEM-Repaint einplant.


======================================================================
71. Cover-Cache
======================================================================

Beim Seek wird derselbe Titel neu gestartet.

FolderMP3 soll deshalb nicht jedes Mal dasselbe APIC-Bild neu lesen und
dekodieren.

LoadCoverForPath verwendet einen Cache.

Ein Cache-Treffer gilt nur, wenn zum Pfad auch die tatsächlichen
GpImage-/Stream-Objekte noch leben.


======================================================================
72. Cover funktioniert direkt aus ADS
======================================================================

Im ADS-Modus erzeugt PlaySelected bereits vor dem Coveraufruf:

    fullPath = FolderMP3.exe:track.mp3

Dieser identische fullPath geht an:

    LoadCoverForPath

Dadurch arbeitet der vorhandene ID3/PIC/APIC-Pfad direkt mit dem
NTFS-Datenstrom.

Es gibt keinen separaten ADS-Coverdecoder und keinen Temp-Export.


======================================================================
73. 400x400-Hover verwendet dasselbe Bild
======================================================================

Das große Hover-Cover lädt die MP3 nicht erneut.

Es verwendet dasselbe:

    coverGpImage

wie die kleine 34x34-Anzeige.

Dadurch entstehen beim Hover weder zusätzlicher Datei-I/O noch erneutes
JPEG-/PNG-Decoding.


======================================================================
74. Hover-Erkennung durch Subclassing
======================================================================

Das kleine Cover-Control wird mit SetWindowLongPtrW unterklassifiziert.

Die neue Fensterprozedur ist:

    CoverControlProc

Bei WM_MOUSEMOVE wird TrackMouseEvent mit TME_LEAVE aktiviert.

Damit meldet Windows später:

    WM_MOUSELEAVE

Der erste Mouse-Move zeigt das Popup, Mouse-Leave blendet es wieder aus.


======================================================================
75. Das große Cover ist ein rahmenloses Popup
======================================================================

Das Hover-Cover ist ein WS_POPUP-Fenster mit 400x400 Pixeln.

Es benutzt unter anderem:

    WS_EX_TOPMOST
    WS_EX_TOOLWINDOW
    WS_EX_NOACTIVATE

Dadurch ist es sichtbar über normalen Fenstern, erscheint aber nicht wie
ein zweites Hauptfenster und stiehlt beim Anzeigen nicht unnötig den Fokus.


======================================================================
76. Positionierung des Hover-Covers
======================================================================

ShowCoverPopup ermittelt die Bildschirmposition des kleinen Covers.

Bevorzugt wird:

1. rechts daneben
2. darunter
3. links daneben

Falls nötig wird die Position an den Bildschirm angepasst.

Damit bleibt das Popup auch brauchbar, wenn der Player nahe am rechten
oder unteren Bildschirmrand steht.


======================================================================
77. Große Arbeitsbereiche in .bss
======================================================================

Große Puffer, die beim Programmstart nur Nullinhalt benötigen, liegen in
einer uninitialisierten .bss-Section.

Beispiele sind verschiedene Pfad-, Find- und Cover-Arbeitsbereiche.

Dadurch muss nicht dieselbe Menge Nullbytes physisch in der EXE gespeichert
werden.

Diese Aufteilung war ein wichtiger Teil der Größenoptimierung.


======================================================================
78. Entfernte Standalone-Bestandteile
======================================================================

Der fertige GUI-Player benötigt viele alte Bestandteile des ursprünglichen
Decoder-Testprogramms nicht.

Aus dem späteren Cleanup wurden deshalb unter anderem alte Pfade für:

    Kommandozeilen-Testbetrieb
    WAV-Export
    PCM-Datei-Verifikation
    nicht mehr benötigte Konsolen-/Testhelfer

entfernt, soweit sie für den aktiven Playerpfad nicht erforderlich waren.

Der eigentliche Decoderkern blieb erhalten.


======================================================================
79. Warum einige historische Textlabels trotzdem existieren können
======================================================================

Im Legacy-Decodercode können noch Textlabels mit alten Testnamen referenziert
werden.

Das bedeutet nicht automatisch, dass FolderMP3 noch eine sichtbare
Testkonsole besitzt.

Bei Assembler gilt aber:

    Ein referenziertes Label muss definiert bleiben.

Deshalb darf man vermeintliche Testdaten nicht allein nach ihrem Namen
löschen, ohne vorher die tatsächlichen Referenzen zu prüfen.


======================================================================
80. Windows-Systembibliotheken
======================================================================

FolderMP3 verwendet nur Windows-Systembibliotheken, unter anderem:

    KERNEL32.DLL
        Dateien, Mapping, Threads, Verzeichnisse, Stream-Enumeration

    USER32.DLL
        Fenster, Controls, Systemmenü, MessageBox, Timer

    GDI32.DLL
        Schrift und einfache 2D-Zeichnung

    WINMM.DLL
        waveOut-Audio

    GDIPLUS.DLL
        JPEG-/PNG-Decoding und Coverdarstellung

    SHLWAPI.DLL
        SHCreateMemStream

Für diese Architektur wird kein zusätzliches GUI- oder Audioframework
mitgeliefert.


======================================================================
81. Bewusst nicht enthalten
======================================================================

FolderMP3 ist absichtlich kein Universalplayer.

Nicht enthalten sind beispielsweise:

    WAV-/FLAC-/AAC-/OGG-Wiedergabe
    Equalizer
    Visualisierung
    Skin-System
    Medienbibliothek
    Datenbank
    "Add Files"
    rekursive Vollsuche ganzer Laufwerke
    eigener JPEG-/PNG-Decoder
    eigener About-Dialog


======================================================================
82. Besonders empfindliche Stellen
======================================================================

Bei späteren Änderungen sollte man besonders vorsichtig sein bei:

1. Win64-ABI und Stack-Alignment
2. Low-Address-Mapping für 32-Bit-Decoderpointer
3. waveOut-Threadbesitz und Callback-Lebensdauer
4. decoder_generation bei Auto-Next
5. Layer-III-Pre-Roll beim Seek
6. coverStream-Lebensdauer zusammen mit coverGpImage
7. RCX/RDX-Argumentregister bei handgeschriebenen Win64-Aufrufen
8. Trennung von browserFolder und currentFolder
9. playlistMode bei ADS- und Ordnerquelle
10. WM_HSCROLL für Lautstärke und Seek
11. WM_SYSCOMMAND-Maskierung für Always on Top


======================================================================
83. Gute Einstiegspunkte im Source
======================================================================

Für einen ersten Rundgang durch den Source eignen sich:

    start
        PE64-Start und Hauptfenster

    WndProc
        Nachrichtenverteilung

    OnCreate
        GUI, Systemmenü und Start-Playlist

    DrawTransportButton
        Owner-Draw-GDI

    OnCommand
        Buttons, Playlist, Ordner und About

    ToggleAlwaysOnTop
        natives Topmost-Umschalten

    LoadEmbeddedPlaylist
        EXE-ADS als Playlist

    IsMp3NamedDataStream
        Erkennung von :*.mp3:$DATA

    CopyNamedStreamDisplayName
        Streamname -> sichtbarer MP3-Name

    SwitchToFolderPlaylist
        ADS -> normale Ordner-CD

    LoadPlaylist
        normale *.mp3-Dateien

    LoadFolderBrowser
        Pseudo-Tree-Browser

    ChangeFolderFromBrowser
        neue Ordner-CD

    PlaySelected
        Quellenabhängige Pfadbildung und Trackstart

    DecoderThreadProc
        Worker-Lebenszyklus

    DecoderCalcLength
        Länge

    DecoderApplyInitialSeek
        CBR/VBR-Seek und Pre-Roll

    AudioCallback
        waveOut-Pufferkreislauf

    FindCoverInMappedFile
        ID3 PIC/APIC

    LoadCoverForPath
        Cover und Cache

    DrawCoverControl
        34x34-Cover

    CoverControlProc
    ShowCoverPopup
    CoverPopupProc
        400x400-Hover


======================================================================
84. Versionsentwicklung der sichtbaren Funktionen
======================================================================

1.0:
    stabiler Win64-MP3-Player mit nocash-Decoder, Seek, Browser,
    Cover und 400x400-Hover

1.1:
    Zeitanzeige nicht mehr auf 99 Minuten begrenzt

1.1.1:
    "Always on Top" im nativen Windows-Systemmenü

1.2:
    MP3-Dateien als NTFS-ADS direkt an der eigenen EXE;
    ADS beim Start bevorzugt, normale Ordnerfunktion bleibt Fallback


======================================================================
85. Kurz zusammengefasst
======================================================================

FolderMP3 1.2 kombiniert mehrere bewusst einfache Lösungen:

    Standard-Win32-Controls statt GUI-Framework
    Owner-Draw-GDI statt Buttongrafiken
    MessageBoxW statt eigenem About-Fenster
    ListBox-Pseudo-Tree statt komplexem TreeView
    natives Systemmenü statt eigener Topmost-Titelleiste
    Ordner statt Medienbibliothek
    optional EXE-ADS als erste Musikquelle
    direkte ADS-Pfade statt Temp-Export
    Worker-Thread statt Decodieren im GUI-Thread
    nocash-Decoder statt MCI
    waveOut statt großem Multimediaframework
    Low-Address-Mapping statt kompletter 64-Bit-Umschreibung des Decoders
    GDI+ statt eigenem JPEG-/PNG-Decoder
    dasselbe GpImage für kleines und großes Cover
    Decoder-Neustart + Pre-Roll für robusten Seek

Die ADS-Erweiterung ist dabei besonders elegant, weil sie kein zweites
Audio- oder Cover-Subsystem benötigt.

Sie erweitert lediglich die Quelle, aus der ein vollständiger MP3-Pfad
gebildet wird. Ab diesem Punkt benutzt FolderMP3 wieder die bereits
vorhandene und getestete Decoder-, Seek-, Cover- und waveOut-Architektur.


======================================================================
Ende der technischen Dokumentation
======================================================================
